﻿using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using RaytracerLib.MathLib;
using TomanuExtensions;
using RaytracerLib;
using System;

namespace RaytracerTest
{
    [TestClass]
    public class Matrix3Test : TestBase
    {
        [TestMethod]
        public void Matrix3_Test()
        {
            string str = ColorFloat.SRGB_Red.ToString();
            double[] ar = { 0, 1, -1, 12345656764564343, -1435434343543536e14 };

            int[] perm1 = new int[4];
            int[] perm2 = new int[4];

            do
            {
                var m1d = Microsoft.DirectX.Matrix.Zero;
                m1d.M11 = (float)ar[perm1[0]];
                m1d.M12 = (float)ar[perm1[1]];
                m1d.M13 = (float)ar[perm1[2]];
                m1d.M21 = (float)ar[perm1[3]];
                m1d.M22 = (float)ar[perm1[1]];
                m1d.M23 = (float)ar[perm1[2]];
                m1d.M31 = (float)ar[perm1[0]];
                m1d.M32 = (float)ar[perm1[1]];
                m1d.M33 = (float)ar[perm1[0]];

                var m1 = new Matrix3(ar[perm1[0]],
                                     ar[perm1[1]],
                                     ar[perm1[2]],
                                     ar[perm1[3]],
                                     ar[perm1[1]],
                                     ar[perm1[2]],
                                     ar[perm1[0]],
                                     ar[perm1[1]],
                                     ar[perm1[0]]);

                var m1dt = Microsoft.DirectX.Matrix.TransposeMatrix(m1d);

                do
                {
                    var m2d = Microsoft.DirectX.Matrix.Zero;
                    m2d.M11 = (float)ar[perm2[0]];
                    m2d.M12 = (float)ar[perm2[1]];
                    m2d.M13 = (float)ar[perm2[2]];
                    m2d.M21 = (float)ar[perm2[3]];
                    m2d.M22 = (float)ar[perm2[1]];
                    m2d.M23 = (float)ar[perm2[2]];
                    m2d.M31 = (float)ar[perm2[0]];
                    m2d.M32 = (float)ar[perm2[1]];
                    m2d.M33 = (float)ar[perm2[0]];

                    var m2 = new Matrix3(ar[perm2[0]],
                                         ar[perm2[1]],
                                         ar[perm2[2]],
                                         ar[perm2[3]],
                                         ar[perm2[1]],
                                         ar[perm2[2]],
                                         ar[perm2[0]],
                                         ar[perm2[1]],
                                         ar[perm2[0]]);

                    // Add
                    {
                        var m3 = m1 + m2;
                        var m3d = m1d + m2d;
                        AreAlmostRelativeEquals(m3d, m3);
                    }

                    // Sub
                    {
                        var m3 = m1 - m2;
                        var m3d = m1d - m2d;
                        AreAlmostRelativeEquals(m3d, m3);
                    }

                    // Mul matrix
                    {
                        var m3 = m1 * m2;
                        var m3d = m1d * m2d;
                        AreAlmostRelativeEquals(m3d, m3);
                    }

                    // Transpose
                    {
                        var m3 = m1.Transposed;
                        var m3d = Microsoft.DirectX.Matrix.TransposeMatrix(m1d);
                        AreAlmostRelativeEquals(m3d, m3);
                    }

                    // Mul vector
                    {
                        var vd = new Microsoft.DirectX.Vector3(
                            (float)m2[0], (float)m2[1], (float)m2[2]);
                        var v = new Vector3(m2[0], m2[1], m2[2]);
                        var r1 = m1 * v;
                        var r2 = Microsoft.DirectX.Vector3.Transform(vd, m1dt);
                        AreAlmostRelativeEquals(r2, r1);
                    }

                    // Mul point
                    {
                        var vd = new Microsoft.DirectX.Vector3(
                            (float)m2[0], (float)m2[1], (float)m2[2]);
                        var v = new Vector3(m2[0], m2[1], m2[2]);
                        var r1 = m1 * v;
                        var r2 = Microsoft.DirectX.Vector3.Transform(vd, m1dt);
                        var r3 = r1;
                        AreAlmostRelativeEquals(r2, r3);
                    }

                    // Equality.
                    {
                        bool b1 = m1d.Equals(m2d);
                        bool b2 = m1d == m2d;
                        bool b3 = m1.Equals(m2);
                        bool b4 = m1 == m2;

                        Assert.IsTrue(b1 == b2);
                        Assert.IsTrue(b2 == b3);
                        Assert.IsTrue(b3 == b4);
                    }
                }
                while (TestBase.IncCounter(perm1, perm1.Length, ar.Length));
            }
            while (TestBase.IncCounter(perm2, perm2.Length, ar.Length));

            // Equal.
            {
                var m1 = new Matrix3(0, 0, 0, 0, 0, 0, 0, 0, 0);
                var m2 = new Matrix3(0, 0, 0, 0, 0, 0, 0, 0, 0);
                var ar1 = m1.ToArray();

                Assert.IsTrue(m1.Equals(m1));

                for (int x = 0; x < 3; x++)
                {
                    for (int y = 0; y < 3; y++)
                    {
                        ar1[x + y * 3] = 1;
                        m1 = new Matrix3(ar1);

                        Assert.IsFalse(m1 == m2);
                        Assert.IsTrue(m1 != m2);
                        Assert.IsFalse(m1.Equals(m2));

                        m2 = new Matrix3(ar1);

                        Assert.IsTrue(m1 == m2);
                        Assert.IsFalse(m1 != m2);
                        Assert.IsTrue(m1.Equals(m2));

                        ar1[x + y * 3] = 0;
                        m1 = new Matrix3(ar1);
                        m2 = new Matrix3(ar1);
                    }
                }
            }

            // Determinant
            {
                AreAlmostRelativeEquals(12, new Matrix3(1, 2, 1, 3, 2, 4, 3, 2, 1).Determinant);
                AreAlmostRelativeEquals(6, new Matrix3(2, 2, 1, 3, 2, 4, 3, 2, 1).Determinant);
            }

            // Invert
            {
                var m1 = new Matrix3(1, 2, 1, 3, 2, 4, 3, 2, 1);
                var m2 = m1.Inverted;
                var m3 = new Matrix3(-6.0 / 12, 0, 6.0 / 12,
                                       9.0 / 12, -2.0 / 12, -1.0 / 12,
                                       0, 4.0 / 12, -4.0 / 12);

                AreAlmostRelativeEquals(m3, m2);
            }
            {
                var m1 = new Matrix3(2, 2, 1, 3, 2, 4, 3, 2, 1);
                var m2 = m1.Inverted;
                var m3 = new Matrix3(-6.0 / 6, 0, 6.0 / 6,
                                       9.0 / 6, -1.0 / 6, -5.0 / 6,
                                       0, 2.0 / 6, -2.0 / 6);

                AreAlmostRelativeEquals(m3, m2);
            }

            // Rotate
            {
                Vector2 p = new Vector2(2, 3);
                Matrix3 mr1 = Matrix3.CreateRotate(-10);
                Matrix3 mr2 = Matrix3.CreateRotate(10);
                var p1 = mr1 * p;
                var p2 = mr2 * p;

                AreAlmostRelativeEquals(new Vector2(1.44867097302363, 3.30171961437048), p1);
                AreAlmostRelativeEquals(new Vector2(2.49056003902521, 2.60712690370276), p2);
            }

            // Translate
            {
                Vector2 p = new Vector2(2, 3);
                Matrix3 mr1 = Matrix3.CreateTranslation(-1, -3);
                Vector2 p1 = mr1 * p;
                AreAlmostRelativeEquals(new Vector2(1, 0), p1);
                Assert.AreEqual(new Vector2(-1, -3), mr1.Col3);
            }
        }
    }
}